import cache from 'expire-cache';
import { Loading } from 'gantd';
import { isEmpty as _isEmpty, isEqual, omit, pick as _pick } from 'lodash';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { dynamic } from 'umi';
import type { AnchorListItem, ContentItem, ListItem, MenuFilter } from './index';

const time = 60 * 60 * 8;
export const contextMenuCache = cache.namespace('contextAnchor', time);
export const renderStateCache = cache.namespace('contextAnchorRenderState', time);
export const layoutCache = cache.namespace('contextAnchorLayout', time);
export const heightCache = cache.namespace('contextAnchorHeight', time);

export interface ParseContextPathRes {
  compPath: string; // 页面或者组件的路径
  modelPath: string; // 对应页面配置的model路径
  itemKey: string; //
  countKey: string; // 用于获取统计信息的key
  menufilter: string | string[]; // 菜单的过滤箱
  height: number;
  resizable: boolean;
  minHeight: number;
  maxHeight: number;
}

/**
 * 根据上下文菜单路径解析出数据
 * @param str
 */
export const parseContextPath = (path: string): ParseContextPathRes => {
  let arg;
  const strArr = path.split('?');
  let str = strArr[1];
  if (str) {
    try {
      str = strArr[1].replace(/&quot;/g, '"');
      arg = JSON.parse(str);
    } catch (err) {
      console.error(`${tr('上下文菜单JSON参数解析错误')}=`, err, str);
      return {} as any;
    }
  }
  return { compPath: strArr[0].replace('ui-prd', 'ibom'), ...arg };
};

/**
 * 解析上下文菜单，配置动态加载组件
 * @param list 上下文菜单列表
 * @param loadingHeight 额外参数
 */
export const pareseContextMenu = (list: any[]) => {
  const res = list.map((item) => {
    const { id, name, path, icon } = item;
    const { compPath, menufilter: itemFilter, height, ...res } = parseContextPath(path);
    const pageUrl = compPath.replace('.tsx', '');
    return {
      id,
      title: name,
      icon,
      component: dynamic({
        loader: () => import(`@/pages/${pageUrl}.tsx`),
        loading: () => <Loading height={height} index={2} />,
      }),
      menufilter: itemFilter,
      height,
      ...res,
    };
  });
  return res;
};

/**
 * 过滤上下文菜单
 * @param list
 * @param menufilter
 */
export const filterContextMenu = (list: any[], menufilter: MenuFilter) => {
  if (_isEmpty(menufilter)) return list;
  const res = list.filter((item) => {
    const { menufilter: itemFilter } = item;
    if (_isEmpty(itemFilter)) return true;
    const filterFields = Object.keys(itemFilter);
    const values = _pick(menufilter, filterFields);

    return filterFields.every((item) => {
      const currentValue = values[item];
      const filter = itemFilter[item];
      if (item.indexOf('NOT_') > -1) {
        return !isEqual(filter, currentValue);
      }
      if (Array.isArray(filter)) {
        return filter.includes(currentValue);
      } else {
        return isEqual(filter, currentValue);
      }
    });
  });
  return res;
};

/**
 * 获取锚点列表和对应渲染内容列表
 * @param allContextList 所有渲染项列表
 * @param filterContextList 过滤的渲染项列表
 * @param showInvalidMenu 是否显示过滤掉的菜单
 */
export const getAnchorsAndRenderContents = (
  allContextList: AnchorListItem[],
  filterContextList: AnchorListItem[],
  showInvalidMenu: boolean,
) => {
  let resList: ListItem[] = [];
  const resContent: ContentItem[] = [];
  const idMap = {};
  const baseKeys = ['id', 'title'];
  // 菜单链接独有属性key
  const listKeys = ['key', 'complete', 'itemKey', 'count', 'icon'];

  filterContextList.forEach((item) => {
    idMap[item.id] = true;
    resList.push(_pick(item, [...baseKeys, ...listKeys]) as AnchorListItem);
    resContent.push(omit(item, listKeys) as ContentItem);
  });

  if (showInvalidMenu) {
    resList = allContextList.map((item) => {
      return {
        ...(_pick(item, [...baseKeys, ...listKeys]) as AnchorListItem),
        isInvalid: !idMap[item.id],
      };
    });
  }

  return [resList, resContent];
};

/**
 * 获取全部锚点列表（包括无效menu）
 * @param list
 */
export const getAllAnchors = (
  originalContextList: AnchorListItem[],
  contextList: AnchorListItem[],
  preffixList: AnchorListItem[],
  suffixList: AnchorListItem[],
) => {
  const anchorListItem = [...preffixList, ...originalContextList, ...suffixList];
  const resList: ListItem[] = [];
  anchorListItem.forEach((item) => {
    const { key, id, title, complete, itemKey, count, icon, ...res } = item;
    const isFlag = [...preffixList, ...contextList, ...suffixList].find((val) => val.id == item.id);
    resList.push({
      key,
      id,
      title,
      complete,
      itemKey,
      count,
      icon,
      isInvalid: isFlag ? false : true,
    });
  });
  return resList;
};

/**
 * 设置当前页面文本是否可选
 * @param selectable
 */
function setTextSelectable(selectable: boolean) {
  document.onselectstart = () => selectable;
}

export type UseResizeProps = {
  height: number; // 当前容器高度
  minHeight: number; // 最小高度
  maxHeight?: number; // 最大高度
  onSizeChange: (height: number) => void; // 当高度变化的回调
};

const MASK_SIZE = 100;

export function useResize(props: UseResizeProps) {
  const { height, minHeight, maxHeight, onSizeChange } = props;

  // 标识当前是否在拖拽中
  const [resizing, setResizing] = useState(false);
  const resizingRef = useRef(false);
  // 记录被拖拽元素的位置
  const initDragStateRef = useRef({
    y: 0,
    height,
  });
  const [resizingHeihgt, setResizingHeight] = useState(0);
  const [maskPosition, setMaskPosition] = useState({});

  function updateMaskPosition(e: MouseEvent) {
    setMaskPosition({
      left: e.clientX - 0.5 * MASK_SIZE,
      top: e.clientY - 0.5 * MASK_SIZE,
      width: MASK_SIZE,
      height: MASK_SIZE,
    });
  }

  const onMouseDown = useCallback(
    (e: MouseEvent) => {
      setResizing(true);
      resizingRef.current = true;
      setTextSelectable(false);
      initDragStateRef.current = {
        y: e.clientY,
        height,
      };
      onSizeChange(height);
      setResizingHeight(height);
      updateMaskPosition(e);
    },
    [height],
  );

  const onMouseMove = useCallback((e: MouseEvent) => {
    if (resizingRef.current) {
      const { y, height } = initDragStateRef.current;
      const diffY = e.clientY - y;
      const newHeight = height + diffY;

      setResizingHeight(newHeight);
      updateMaskPosition(e);

      // 拖拽后的高度超出限制
      const isOverMax = maxHeight && maxHeight < newHeight;
      const isOverMin = minHeight && minHeight > newHeight;
      if (isOverMax || isOverMin) return;

      onSizeChange(newHeight);
    }
  }, []);

  const onMouseUp = useCallback(() => {
    setResizing(false);
    resizingRef.current = false;
    setTextSelectable(true);
  }, []);

  useEffect(() => {
    const eventName = 'mousemove';
    window.addEventListener(eventName, onMouseMove);
    return () => {
      window.removeEventListener(eventName, onMouseMove);
    };
  }, []);

  useEffect(() => {
    const eventName = 'mouseup';
    window.addEventListener(eventName, onMouseUp);
    return () => {
      window.removeEventListener(eventName, onMouseUp);
    };
  }, []);

  return {
    onMouseDown,
    resizing,
    resizingHeihgt,
    maskPosition,
  } as any;
}
